#ifndef __LOADER_H__
#define __LOADER_H__

#include "ibase_impl.h"
#include "module_holder.h"
#include "../components/common/include/registry_ifaces.h"
#include "../components/common/include/class_factory_ifaces.h"
#include "../components/common/include/cls_ids.h"
#include "ivariant_impl.h"
#include "ivariant_helper.h"
#include "iservice_locator_impl.h"

#include <string>
#include <memory>

#define BOSS_SERVICE_REGISTRY_MODULE_NAME	"service_registry"
#define BOSS_CLASS_FACTORY_MODULE_NAME	"class_factory"

#ifdef _WIN32
#define BOSS_SERVICE_MODULE_PREF ""
#define BOSS_SERVICE_MODULE_EXT ".dll"
#define BOSS_SERVICE_REGISTRY_MODULE_FULL_NAME \
  BOSS_SERVICE_MODULE_PREF BOSS_SERVICE_REGISTRY_MODULE_NAME BOSS_SERVICE_MODULE_EXT
#define BOSS_CLASS_FACTORY_MODULE_FULL_NAME \
  BOSS_SERVICE_MODULE_PREF BOSS_CLASS_FACTORY_MODULE_NAME BOSS_SERVICE_MODULE_EXT
#else
#ifdef _OS_NIX
#define BOSS_SERVICE_MODULE_PREF "lib"
#define BOSS_SERVICE_MODULE_EXT ".so"
#define BOSS_SERVICE_REGISTRY_MODULE_FULL_NAME \
  BOSS_SERVICE_MODULE_PREF BOSS_SERVICE_REGISTRY_MODULE_NAME BOSS_SERVICE_MODULE_EXT
#define BOSS_CLASS_FACTORY_MODULE_FULL_NAME \
  BOSS_SERVICE_MODULE_PREF BOSS_CLASS_FACTORY_MODULE_NAME BOSS_SERVICE_MODULE_EXT
#else
#error Unsupporetd OS
#endif
#endif


namespace Boss
{

  BOSS_DECLARE_RUNTIME_EXCEPTION(Loader)

  class Loader
    : public NonCopyable
  {
  public:
    Loader(const std::string &pathToBossBin, const std::string &registry)
    {
      Init(
        pathToBossBin + BOSS_SERVICE_REGISTRY_MODULE_FULL_NAME,
        BOSS_CLSID_ISERVICEREGISTRYIMPL,
        pathToBossBin + BOSS_CLASS_FACTORY_MODULE_FULL_NAME,
        BOSS_CLSID_ICLASSFACTORYIMPL,
        registry);
    }
    Loader(const std::string &registryModule, const std::string &registryCLSID,
      const std::string &classFactoryModule, const std::string &classFactoryCLSID,
      const std::string &registry)
    {
      Init(registryModule, registryCLSID, classFactoryModule, classFactoryCLSID, registry);
    }
    ~Loader()
    {
      SysServiceLocator->DelService(BOSS_CLSID_ICLASSFACTORYIMPL);
      ClassFactory.Release();
      RegistryCtrl.Release();
      SysServiceLocator.Release();
      ClassFactoryModule->SetEnviroment(RefObjPtr<IBase>(0));
      RegistryModule->SetEnviroment(RefObjPtr<IBase>(0));
      ClassFactoryModule.reset(0);
      RegistryModule.reset(0);
    }
    void RegModule(const std::string &path)
    {
      RefObjPtr<IVariant> Path = IBaseImpl<IVariantImpl>::Create();
      IVariantHelper PathHelper(Path);
      PathHelper = path.c_str();
      if (RegistryCtrl->RegModule(Path.Get()) != rcOk)
        throw LoaderException("Can't reg module");
    }
    void UnRegModule(const std::string &path)
    {
      RefObjPtr<IVariant> Path = IBaseImpl<IVariantImpl>::Create();
      IVariantHelper PathHelper(Path);
      PathHelper = path.c_str();
      if (RegistryCtrl->UnRegModule(Path.Get()) != rcOk)
        throw LoaderException("Can't unreg module");
    }
    template <typename T>
    RefObjPtr<T> CreateObject(TGuid coClassId)
    {
      RefObjPtr<IBase> Instance;
      if (ClassFactory->CreateObject(coClassId, reinterpret_cast<TPtr *>(Instance.GetPPtr())) != rcOk)
        throw LoaderException("Can't create object");
      RefObjQIPtr<T> Ret(Instance);
      if (!Ret.Get())
        throw LoaderException("Interface not found");
      return Ret;
    }
    RefObjPtr<IServiceLocator> GetSysServiceLocator() const
    {
      return SysServiceLocator;
    }

  private:
    typedef std::auto_ptr<ModuleHolder> ModuleHolderPtr;

    RefObjPtr<IServiceLocator> SysServiceLocator;

    ModuleHolderPtr RegistryModule;
    ModuleHolderPtr ClassFactoryModule;

    RefObjQIPtr<IServiceRegistryCtrl> RegistryCtrl;
    RefObjQIPtr<IClassFactory> ClassFactory;

    void Init(const std::string &registryModule, const std::string &registryCLSID,
      const std::string &classFactoryModule, const std::string &classFactoryCLSID,
      const std::string &registry)
    {
      SysServiceLocator = IBaseImpl<IServiceLocatorImpl>::Create();

      ModuleHolderPtr NewRegistryModule(new ModuleHolder(ModuleHolder::DllHolderPtr(new DllHolder(registryModule))));
      NewRegistryModule->SetEnviroment(RefObjQIPtr<IBase>(SysServiceLocator));

      RefObjPtr<IBase> RegistryInst = NewRegistryModule->CreateObject(registryCLSID.c_str());
      RefObjQIPtr<IServiceRegistryCtrl> NewRegCtrl(RegistryInst);
      if (!NewRegCtrl.Get())
        throw LoaderException("Interface IServiceRegistryCtrl not found");
      RefObjQIPtr<IServiceRegistry> NewReg(RegistryInst);
      if (!NewReg.Get())
        throw LoaderException("Interface IServiceRegistry not found");

      ModuleHolderPtr NewClassFactoryModule(new ModuleHolder(ModuleHolder::DllHolderPtr(new DllHolder(classFactoryModule))));
      NewClassFactoryModule->SetEnviroment(RefObjQIPtr<IBase>(SysServiceLocator));

      RefObjPtr<IBase> ClassFactoryInst = NewClassFactoryModule->CreateObject(classFactoryCLSID.c_str());
      RefObjQIPtr<IClassFactoryCtrl> NewClassFactoryCtrl(ClassFactoryInst);
      if (!NewClassFactoryCtrl.Get())
        throw LoaderException("Interface IClassFactoryCtrl not found");
      RefObjQIPtr<IClassFactory> NewClassFactory(ClassFactoryInst);
      if (!NewClassFactory.Get())
        throw LoaderException("Interface IClassFactory not found");

      RefObjPtr<IVariant> RegPath = IBaseImpl<IVariantImpl>::Create();
      IVariantHelper RegPathHelper(RegPath);
      RegPathHelper = registry.c_str();
      TResult Code = NewRegCtrl->Init(RegPath.Get());
      if (Code != rcOk && Code != rcFalse)
        throw LoaderException("Can't registry initialize");

      if (NewClassFactoryCtrl->SetRegistry(NewReg.Get()) != rcOk)
        throw LoaderException("Can't init class factory");

      SysServiceLocator->AddService(BOSS_CLSID_ICLASSFACTORYIMPL, NewClassFactory.Get());

      ClassFactoryModule = NewClassFactoryModule;
      ClassFactory = NewClassFactory;
      RegistryModule = NewRegistryModule;
      RegistryCtrl = NewRegCtrl;
    }
  };

}

#endif  // !__LOADER_H__
